package impl

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"time"

	"atomgit.com/openlinksaas/extension-proto-gen-go.git/extension_fs_api"
	"atomgit.com/openlinksaas/tencent-storage/config"
	cos "github.com/tencentyun/cos-go-sdk-v5"
)

type ApiImpl struct {
	extension_fs_api.UnimplementedExtensionFsApiServer
	cosClient *cos.Client
}

func NewApiImpl(cfg *config.ServerConfig) (*ApiImpl, error) {
	u, err := url.Parse(cfg.Url)
	if err != nil {
		return nil, err
	}

	client := cos.NewClient(&cos.BaseURL{BucketURL: u}, &http.Client{
		Timeout: 300 * time.Second,
		Transport: &cos.AuthorizationTransport{
			SecretID:  cfg.SecretId,
			SecretKey: cfg.SecretKey,
		},
	})
	return &ApiImpl{cosClient: client}, nil
}

func (apiImpl *ApiImpl) ReadFile(req *extension_fs_api.ReadFileRequest, s extension_fs_api.ExtensionFsApi_ReadFileServer) error {
	getRes, err := apiImpl.cosClient.Object.Get(s.Context(), fmt.Sprintf("%s/%s", req.FsId, req.FileId), nil)
	if err != nil {
		return err
	}
	defer getRes.Body.Close()

	readBuf := make([]byte, 1024*1024)
	for {
		n, err := getRes.Body.Read(readBuf)
		if n > 0 {
			s.Send(&extension_fs_api.ReadFileResponse{
				Data: readBuf[:n],
			})
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
	}
	return nil
}

func (apiImpl *ApiImpl) WriteFile(s extension_fs_api.ExtensionFsApi_WriteFileServer) error {
	var fsId string
	var fileId string
	var tmpFile *os.File

	defer func() {
		if tmpFile != nil {
			os.Remove(tmpFile.Name())
			tmpFile.Close()
		}
	}()

	for {
		req, err := s.Recv()
		if err == io.EOF {
			break
		}
		if err != nil {
			return err
		}
		if tmpFile == nil {
			fsId = req.FsId
			fileId = req.FileId
			tmpFile, err = os.CreateTemp(os.TempDir(), "upload_*")
			if err != nil {
				return err
			}
		}
		_, err = tmpFile.Write(req.Data)
		if err != nil {
			return err
		}
	}

	if tmpFile == nil {
		return fmt.Errorf("wrong request")
	}
	_, err := tmpFile.Seek(0, io.SeekStart)
	if err != nil {
		return err
	}

	name := fmt.Sprintf("%s/%s", fsId, fileId)
	_, err = apiImpl.cosClient.Object.Put(s.Context(), name, tmpFile, nil)
	if err != nil {
		return err
	}
	s.SendAndClose(&extension_fs_api.WriteFileResponse{})
	return nil
}

func (apiImpl *ApiImpl) CopyFile(ctx context.Context, req *extension_fs_api.CopyFileRequest) (*extension_fs_api.CopyFileResponse, error) {
	srcUrl := fmt.Sprintf("%s/%s/%s", apiImpl.cosClient.BaseURL.BucketURL.Host, req.FromFsId, req.FromFileId)
	destName := fmt.Sprintf("%s/%s", req.ToFsId, req.ToFileId)
	_, _, err := apiImpl.cosClient.Object.Copy(ctx, destName, srcUrl, nil)
	if err != nil {
		return nil, err
	}
	return &extension_fs_api.CopyFileResponse{}, nil
}

func (apiImpl *ApiImpl) RemoveFile(ctx context.Context, req *extension_fs_api.RemoveFileRequest) (*extension_fs_api.RemoveFileResponse, error) {
	_, err := apiImpl.cosClient.Object.Delete(ctx, fmt.Sprintf("%s/%s", req.FsId, req.FileId))
	if err != nil {
		return nil, err
	}
	return &extension_fs_api.RemoveFileResponse{}, nil
}
